using System;
using System.Collections.Generic;

namespace RaidLog.Engine.Armory
{

    public class itemClass
    {
        public string itemClassName;
        public item[] items;
    }

    public class item
    {
        public string itemName;
        public string itemID;
        public people[] people;
    }

    public class itemInfoClass : Dictionary<string, Dictionary<string, List<people>>> { }

    public class people
    {
        public string name;
        public DateTime lastSeenOn = DateTime.Now;
        public people() { }
        public people(string _name) { name = _name; }
        public override int GetHashCode()
        {
            return name.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            return name.Equals(((people)obj).name);
        }
    }

    public static class ArmoryProgram
    {


        static Dictionary<string, item_tooltip.pageItemTooltip> itemLookup = new Dictionary<string, item_tooltip.pageItemTooltip>();
        //static itemInfoClass itemData = null;
        static string logFile = "Output.txt";
        static string dataFile = "Data.xml";

        private static void Run()
        {
            itemInfoClass itemData = null;

            if (System.IO.File.Exists(logFile)) System.IO.File.Delete(logFile);
            System.Diagnostics.TextWriterTraceListener _TextWriterTraceListener =
            new System.Diagnostics.TextWriterTraceListener(logFile, "testSource");
            System.Diagnostics.Trace.Listeners.Add(_TextWriterTraceListener);

            UncacheCharacterData();

            itemData = LoadItemData();

            AccumulateGuildData(itemData);

            CompactGuildData(itemData);

            SaveItemData(itemData);

            //PrintSummary(itemData);

            System.Diagnostics.Trace.WriteLine("Protection Paladins");
            System.Diagnostics.Trace.Indent();
            PrintSummary_ProtectionPaladin(itemData);
            System.Diagnostics.Trace.Unindent();

            System.Diagnostics.Trace.WriteLine("Cloth DPS");
            System.Diagnostics.Trace.Indent();
            PrintSummary_ClothDPS(itemData);
            System.Diagnostics.Trace.Unindent();

            System.Diagnostics.Trace.WriteLine("Hunter/Shaman DPS");
            System.Diagnostics.Trace.Indent();
            PrintSummary_HunterShamanDPS(itemData);
            System.Diagnostics.Trace.Unindent();

            System.Diagnostics.Trace.WriteLine("Holy Paladins");
            System.Diagnostics.Trace.Indent();
            PrintSummary_HealingPaladin(itemData);
            System.Diagnostics.Trace.Unindent();

            System.Diagnostics.Trace.WriteLine("Feral Druids");
            System.Diagnostics.Trace.Indent();
            PrintSummary_FearlDruid(itemData);
            System.Diagnostics.Trace.Unindent();

            System.Diagnostics.Trace.Flush();
            _TextWriterTraceListener.Close();
        }

        private static void CompactGuildData(itemInfoClass itemData)
        {
            foreach (KeyValuePair<string, Dictionary<string, List<people>>> itemType in itemData)
            {
                Dictionary<string, List<people>> dataAccum = new Dictionary<string, List<people>>();
                foreach (KeyValuePair<string, List<people>> items in itemType.Value)
                {
                    Dictionary<people, people> maxValues = new Dictionary<people, people>();
                    foreach (people person in items.Value)
                    {
                        if (!maxValues.ContainsKey(person) || maxValues[person].lastSeenOn < person.lastSeenOn)
                        {
                            maxValues[person] = person;
                        }
                    }
                    dataAccum[items.Key] = new List<people>(maxValues.Values);
                }
                foreach (KeyValuePair<string, List<people>> items in dataAccum)
                {
                    itemType.Value[items.Key] = items.Value;
                }
            }
        }

        private static void PrintSummary_ProtectionPaladin(itemInfoClass itemData)
        {
            PrintSummary(itemData, new FilterItemDelegate(delegate(string itemID)
            {
                item_tooltip.pageItemTooltip iteminfo = GetItemInfo(itemID);
                if (null == iteminfo) return false;
                if (iteminfo.equipData[0].subclassName == "Libram" ||
                    iteminfo.equipData[0].subclassName == "Shield") return true;
                if (iteminfo.equipData[0].subclassName != "Plate" &&
                    iteminfo.equipData[0].subclassName != "Other" &&
                    iteminfo.equipData[0].subclassName != "Sword" &&
                    iteminfo.equipData[0].subclassName != "Mace" &&
                    iteminfo.equipData[0].subclassName != "Axe") return false;
                int _tankScore = tankScore(iteminfo);
                int _healScore = healScore(iteminfo);
                int _castScore = castScore(iteminfo);
                int _meleeScore = meleeScore(iteminfo);
                if (_tankScore >= _healScore &&
                    _tankScore >= _castScore &&
                    _tankScore >= _meleeScore) return true;
                return false;
            }));
        }

        private static void PrintSummary_HealingPaladin(itemInfoClass itemData)
        {
            PrintSummary(itemData, new FilterItemDelegate(delegate(string itemID)
            {
                item_tooltip.pageItemTooltip iteminfo = GetItemInfo(itemID);
                if (null == iteminfo) return false;
                if (iteminfo.equipData[0].subclassName == "Libram" ||
                    iteminfo.equipData[0].subclassName == "Shield") return true;
                if (iteminfo.equipData[0].subclassName != "Plate" &&
                    iteminfo.equipData[0].subclassName != "Other" &&
                    iteminfo.equipData[0].subclassName != "Sword" &&
                    iteminfo.equipData[0].subclassName != "Mace" &&
                    iteminfo.equipData[0].subclassName != "Axe") return false;
                int _tankScore = tankScore(iteminfo);
                int _healScore = healScore(iteminfo);
                int _castScore = castScore(iteminfo);
                int _meleeScore = meleeScore(iteminfo);
                if (_healScore >= _tankScore &&
                    _healScore >= _castScore &&
                    _healScore >= _meleeScore) return true;
                return false;
            }));
        }

        private static void PrintSummary_FearlDruid(itemInfoClass itemData)
        {
            PrintSummary(itemData, new FilterItemDelegate(delegate(string itemID)
            {
                item_tooltip.pageItemTooltip iteminfo = GetItemInfo(itemID);
                if (null == iteminfo) return false;
                if (iteminfo.equipData[0].subclassName == "Idol") return true;
                if (iteminfo.equipData[0].subclassName != "Leather" &&
                    iteminfo.equipData[0].subclassName != "Other" &&
                    iteminfo.equipData[0].subclassName != "Dagger" &&
                    iteminfo.equipData[0].subclassName != "Mace" &&
                    iteminfo.equipData[0].subclassName != "Staff") return false;
                int _feralScore = feralScore(iteminfo);
                int _tankScore = tankScore(iteminfo);
                int _healScore = healScore(iteminfo);
                int _castScore = castScore(iteminfo);
                int _meleeScore = meleeScore(iteminfo);
                if (_feralScore > _healScore &&
                    _feralScore >= _tankScore &&
                    _feralScore > _castScore &&
                    _feralScore >= _meleeScore) return true;
                return false;
            }));
        }

        private static void PrintSummary_ClothDPS(itemInfoClass itemData)
        {
            PrintSummary(itemData, new FilterItemDelegate(delegate(string itemID)
            {
                item_tooltip.pageItemTooltip iteminfo = GetItemInfo(itemID);
                if (null == iteminfo) return false;
                if (iteminfo.equipData[0].subclassName != "Cloth" &&
                    iteminfo.equipData[0].subclassName != "Other" &&
                    iteminfo.equipData[0].subclassName != "Sword" &&
                    iteminfo.equipData[0].subclassName != "Mace" &&
                    iteminfo.equipData[0].subclassName != "Staff" &&
                    iteminfo.equipData[0].subclassName != "Wand") return false;
                int _tankScore = tankScore(iteminfo);
                int _healScore = healScore(iteminfo);
                int _castScore = castScore(iteminfo);
                int _meleeScore = meleeScore(iteminfo);
                if (_castScore >= _healScore &&
                    _castScore >= _tankScore &&
                    _castScore >= _meleeScore) return true;
                return false;
            }));
        }

        private static void PrintSummary_HunterShamanDPS(itemInfoClass itemData)
        {
            PrintSummary(itemData, new FilterItemDelegate(delegate(string itemID)
            {
                item_tooltip.pageItemTooltip iteminfo = GetItemInfo(itemID);
                if (null == iteminfo) return false;
                if (iteminfo.equipData[0].subclassName != "Mail" &&
                    iteminfo.equipData[0].subclassName != "Other" &&
                    iteminfo.equipData[0].subclassName != "Sword" &&
                    iteminfo.equipData[0].subclassName != "Mace" &&
                    iteminfo.equipData[0].subclassName != "Staff" &&
                    iteminfo.equipData[0].subclassName != "Totem" &&
                    iteminfo.equipData[0].subclassName != "CrossBow" &&
                    iteminfo.equipData[0].subclassName != "Axe" &&
                    iteminfo.equipData[0].subclassName != "Gun" &&
                    iteminfo.equipData[0].subclassName != "Dagger") return false;
                int _tankScore = tankScore(iteminfo);
                int _healScore = healScore(iteminfo);
                int _castScore = castScore(iteminfo);
                int _meleeScore = meleeScore(iteminfo);
                if ((_castScore > _healScore &&
                    _castScore > _tankScore) ||
                    (_meleeScore > _healScore &&
                    _meleeScore > _tankScore)) return true;
                return false;
            }));
        }

        private static void PrintSummary(itemInfoClass itemData)
        {
            PrintSummary(itemData, new FilterItemDelegate(delegate(string s) { return true; }));
        }

        private static int GetSpellDamage(item_tooltip.pageItemTooltip iteminfo)
        {
            return searchItemToolTip<int>(iteminfo, new searchItemToolTipDelegate<int>(delegate(string spellDesc)
            {
                string regex = @"damage and healing done by magical spells and effects by up to (\d{1,3})";
                System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(spellDesc, regex);
                if (!match.Success) return 0;
                return Int32.Parse(match.Groups[1].Value);
            }));
        }

        private static int GetManaPerFive(item_tooltip.pageItemTooltip iteminfo)
        {
            return searchItemToolTip<int>(iteminfo, new searchItemToolTipDelegate<int>(delegate(string spellDesc)
            {
                string regex = @"Restores (\d{1,3}) mana per 5 sec";
                System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(spellDesc, regex);
                if (!match.Success) return 0;
                return Int32.Parse(match.Groups[1].Value);
            }));
        }

        private static int GetAttackPower(item_tooltip.pageItemTooltip iteminfo)
        {
            return searchItemToolTip<int>(iteminfo, new searchItemToolTipDelegate<int>(delegate(string spellDesc)
            {
                string regex = @"Increases attack power by (\d{1,3})";
                System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(spellDesc, regex);
                if (!match.Success) return 0;
                return Int32.Parse(match.Groups[1].Value);
            }));
        }

        private static int GetHealing(item_tooltip.pageItemTooltip iteminfo)
        {
            return searchItemToolTip<int>(iteminfo, new searchItemToolTipDelegate<int>(delegate(string spellDesc)
            {
                string regex = @"Increases healing done by spells and effects by up to (\d{1,3})";
                System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(spellDesc, regex);
                if (!match.Success) return 0;
                return Int32.Parse(match.Groups[1].Value);
            }));
        }

        private delegate TType searchItemToolTipDelegate<TType>(string effect);
        private static TType searchItemToolTip<TType>(item_tooltip.pageItemTooltip iteminfo, searchItemToolTipDelegate<TType> _searchItemToolTipDelegate)
        {
            if (null != iteminfo.spellData)
            {
                foreach (item_tooltip.pageItemTooltipSpellDataSpell _pageItemTooltipSpellDataSpell in iteminfo.spellData)
                {
                    TType o = _searchItemToolTipDelegate(_pageItemTooltipSpellDataSpell.desc);
                    if (!default(TType).Equals(o)) return o;
                }
            }
            return default(TType);
        }

        private static int meleeScore(item_tooltip.pageItemTooltip iteminfo)
        {
            int score = 0;
            if (iteminfo.bonusAgility != null) score++;
            if (iteminfo.bonusStrength != null) score++;
            if (iteminfo.bonusHitRating != null) score++;
            if (iteminfo.bonusCritRating != null) score++;
            if (GetAttackPower(iteminfo) > 0) score++;
            return score;
        }

        private static int castScore(item_tooltip.pageItemTooltip iteminfo)
        {
            int score = 0;
            if (iteminfo.bonusIntellect != null) score++;
            if (iteminfo.bonusSpirit != null) score++;
            if (iteminfo.bonusHitSpellRating != null) score++;
            if (iteminfo.bonusCritSpellRating != null) score++;
            if (GetSpellDamage(iteminfo) > 0) score++;
            return score;
        }

        private static int healScore(item_tooltip.pageItemTooltip iteminfo)
        {
            int score = 0;
            if (iteminfo.bonusSpirit != null) score++;
            if (iteminfo.bonusIntellect != null) score++;
            if (GetHealing(iteminfo) > 0) score++;
            if (GetManaPerFive(iteminfo) > 0) score++;
            return score;
        }

        private static int tankScore(item_tooltip.pageItemTooltip iteminfo)
        {
            int score = 0;
            if (iteminfo.bonusAgility != null) score++;
            if (iteminfo.bonusStamina != null) score++;
            if (iteminfo.bonusDefenseSkillRating != null) score++;
            if (iteminfo.bonusBlockRating != null) score++;
            if (iteminfo.bonusDodgeRating != null) score++;
            if (iteminfo.bonusParryRating != null) score++;
            if (iteminfo.bonusResilienceRating != null) score++;
            return score;
        }

        private static int feralScore(item_tooltip.pageItemTooltip iteminfo)
        {
            int score = 0;
            if (iteminfo.armor != null) score++;
            if (iteminfo.bonusStrength != null) score++;
            if (iteminfo.bonusAgility != null) score++;
            if (iteminfo.bonusStamina != null) score++;
            if (iteminfo.bonusDefenseSkillRating != null) score++;
            if (iteminfo.bonusDodgeRating != null) score++;
            if (iteminfo.bonusResilienceRating != null) score++;
            if (GetFeralAttackPower(iteminfo) > 0) score++;
            return score;
        }

        private static int GetFeralAttackPower(item_tooltip.pageItemTooltip iteminfo)
        {
            return searchItemToolTip<int>(iteminfo, new searchItemToolTipDelegate<int>(delegate(string spellDesc)
            {
                string regex = @"Increases attack power by (\d{1,4}) in Cat, Bear, Dire Bear, and Moonkin forms only";
                System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(spellDesc, regex);
                if (!match.Success) return 0;
                return Int32.Parse(match.Groups[1].Value);
            }));
        }

        private delegate bool FilterItemDelegate(string itemID);

        private static void PrintSummary(itemInfoClass itemData, FilterItemDelegate filter)
        {
            foreach (KeyValuePair<string, Dictionary<string, List<people>>> itemType in
                GetSortedList<KeyValuePair<string, Dictionary<string, List<people>>>>(itemData,
                delegate(KeyValuePair<string, Dictionary<string, List<people>>> A, KeyValuePair<string, Dictionary<string, List<people>>> B)
                { return A.Key.CompareTo(B.Key); }))
            {
                System.Diagnostics.Trace.WriteLine(String.Format("<HR>[u][b]{0}[/b][/u]", itemType.Key));
                //System.Diagnostics.Trace.WriteLine(itemType.Key);
                System.Diagnostics.Trace.Indent();

                string lastSlotType = null;
                foreach (KeyValuePair<string, List<people>> items in
                    GetSortedList<KeyValuePair<string, List<people>>>(itemType.Value,
                    delegate(KeyValuePair<string, List<people>> A, KeyValuePair<string, List<people>> B)
                    {
                        return firstNonZero(
                          GetItemInfo(A.Key).equipData[0].inventoryType.CompareTo(GetItemInfo(B.Key).equipData[0].inventoryType),
                          GetItemInfo(A.Key).name.CompareTo(GetItemInfo(B.Key).name));
                    }))
                {
                    if (filter(items.Key))
                    {
                        if (null == lastSlotType || GetItemInfo(items.Key).equipData[0].inventoryType != lastSlotType)
                        {
                            if (null != lastSlotType) System.Diagnostics.Trace.WriteLine("");
                            lastSlotType = GetItemInfo(items.Key).equipData[0].inventoryType;
                        }
                        item_tooltip.pageItemTooltip itemInfo = GetItemInfo(items.Key);
                        if (null != itemInfo)
                        {
                            //System.Diagnostics.Trace.WriteLine(items.Key);

                            System.Text.StringBuilder sb = new System.Text.StringBuilder();
                            foreach (people person in GetSortedList<people>(items.Value,
                                delegate(people A, people B)
                                { return A.name.CompareTo(B.name); }))
                            {
                                if (sb.Length != 0) sb.Append(", ");
                                sb.Append(GetPersonBBCode(person.name));
                            }

                            if (String.IsNullOrEmpty(itemInfo.requiredLevel) || Int32.Parse(itemInfo.requiredLevel) > 60)
                            {
                                if (Int32.Parse(itemInfo.overallQualityId) > 3)
                                {
                                    PrintItem_BBItem(items.Key, sb);

                                }
                                else if (Int32.Parse(itemInfo.overallQualityId) > 2)
                                {
                                    //PrintItem_ThottLink(items.Key, sb);
                                    PrintItem_BBItem(items.Key, sb);
                                }
                            }
                        }
                    }
                }
                System.Diagnostics.Trace.Unindent();
            }
        }

        private static void AccumulateGuildData(itemInfoClass itemData)
        {
            guild_info.pageGuildInfoGuild guildInfo = ArmoryUtil.GetGuildInfo("Recursion");
            if (null == guildInfo) return;
            foreach (guild_info.pageGuildInfoGuildMembersCharacter member in guildInfo.members[0].character)
            {
                character_sheet.pageCharacterInfo charInfo = ArmoryUtil.GetCharInfo(member.name);
                if (null == charInfo || null == charInfo.character || null == charInfo.characterTab)
                {
                    System.Diagnostics.Trace.TraceError("Error loading player: " + member.name);
                    continue;
                }
                if (Int32.Parse(charInfo.character[0].level) < 70) continue;
                //System.Diagnostics.Trace.WriteLine(charInfo.character[0].name);
                //System.Diagnostics.Trace.Indent();
                foreach (character_sheet.pageCharacterInfoCharacterTabItemsItem item in charInfo.characterTab[0].items)
                {

                    item_tooltip.pageItemTooltip itemInfo = GetItemInfo(item.id);
                    if (null == itemInfo)
                    {
                        System.Diagnostics.Trace.TraceError("Error loading item: " + item.id);
                        continue;
                    }

                    string subclassName = (null == itemInfo.equipData[0].subclassName) ?
                        "Other" : itemInfo.equipData[0].subclassName;
                    if (!itemData.ContainsKey(subclassName))
                        itemData[subclassName] = new Dictionary<string, List<people>>();
                    if (!itemData[subclassName].ContainsKey(item.id))
                        itemData[subclassName][item.id] = new List<people>();

                    if (!itemData[subclassName][itemInfo.id].Contains(new people(charInfo.character[0].name)))
                        itemData[subclassName][itemInfo.id].Add(new people(charInfo.character[0].name));

                    //System.Diagnostics.Trace.WriteLine(String.Format(
                    //    "{0} ({1}/{2})", itemInfo.name, itemInfo.equipData[0].inventoryType, itemInfo.equipData[0].subclassName));

                }
                //System.Diagnostics.Trace.Unindent();
            }
        }

        private static void SaveItemData(itemInfoClass itemData)
        {
            List<itemClass> itemClassList = new List<itemClass>();
            foreach (KeyValuePair<string, Dictionary<string, List<people>>> itemType in itemData)
            {
                itemClass _itemClass = new itemClass();
                _itemClass.itemClassName = itemType.Key;
                List<item> itemList = new List<item>();
                foreach (KeyValuePair<string, List<people>> itemEntry in itemType.Value)
                {
                    item _item = new item();
                    _item.itemID = itemEntry.Key;
                    _item.itemName = GetItemInfo(itemEntry.Key).name;
                    _item.people = itemEntry.Value.ToArray();
                    itemList.Add(_item);
                }
                _itemClass.items = itemList.ToArray();
                itemClassList.Add(_itemClass);
            }
            System.IO.File.WriteAllText(dataFile, ArmoryUtil.Serialize<itemClass[]>(itemClassList.ToArray()), System.Text.Encoding.Unicode);
        }

        private static itemInfoClass LoadItemData()
        {
            itemInfoClass itemData = new itemInfoClass();
            if (System.IO.File.Exists(dataFile))
            {
                itemClass[] itemDataFromXml = ArmoryUtil.Deserialize<itemClass[]>(System.IO.File.ReadAllText(dataFile, System.Text.Encoding.Unicode));
                if (null != itemDataFromXml)
                {
                    foreach (itemClass _itemClass in itemDataFromXml)
                    {
                        itemData[_itemClass.itemClassName] = new Dictionary<string, List<people>>();
                        foreach (item _item in _itemClass.items)
                        {
                            itemData[_itemClass.itemClassName][_item.itemID] = new List<people>(_item.people);
                        }
                    }
                }
            }
            return itemData;
        }

        private static void UncacheCharacterData()
        {
            foreach (string charInfoCacheFile in System.IO.Directory.GetFiles("cache", "character-sheet*.xml"))
            {
                System.Diagnostics.Trace.WriteLine("Delete: " + charInfoCacheFile);
                System.IO.File.Delete(charInfoCacheFile);
            }
        }

        private static string GetPersonBBCode(string person)
        {
            return String.Format(
                "{0}",
                person);
        }

        private static string GetPersonBBCode_ArmoryLink(string person)
        {
            return String.Format(
                "[url=http://12.129.242.20/character-sheet.xml?r=Perenolde&n={0}]{0}[/url]",
                person);
        }

        private static void PrintItem_BBItem(string itemId, System.Text.StringBuilder sb)
        {
            System.Diagnostics.Trace.WriteLine(String.Format(
                "[item]{0}[/item] - [i]{1}[/i]",
                GetItemInfo(itemId).name,
                sb));
        }

        private static void PrintItem_ThottLink(string itemId, System.Text.StringBuilder sb)
        {
            System.Diagnostics.Trace.WriteLine(String.Format(
                "<a href=\"http://www.thottbot.com/?s={2}\">{0}</a> - [i]{1}[/i]",
                GetItemInfo(itemId).name,
                sb,
                System.Web.HttpUtility.UrlPathEncode(GetItemInfo(itemId).name)));
        }

        private static item_tooltip.pageItemTooltip GetItemInfo(string itemID)
        {
            if (itemLookup.ContainsKey(itemID))
            {
                return itemLookup[itemID];
            }
            else
            {
                item_tooltip.pageItemTooltip itemInfo = ArmoryUtil.GetItemInfo(itemID);
                if (null != itemInfo && null == itemInfo.equipData[0].subclassName)
                    itemInfo.equipData[0].subclassName = "Other";
                itemLookup[itemID] = itemInfo;
                return itemInfo;
            }
        }

        delegate int CompareDelegate<TType>(TType A, TType B);
        static int firstNonZero(params int[] ints)
        {
            foreach (int i in ints)
            {
                if (i != 0) return i;
            }
            return 0;
        }
        static IEnumerable<TType> GetSortedList<TType>(IEnumerable<TType> list, CompareDelegate<TType> comparer)
        {
            List<TType> newList = new List<TType>(list);
            newList.Sort(new Comparison<TType>(comparer));
            return newList;

        }
    }
}